#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
课程表管理工具
功能：
1. 解析教务系统导出的Excel课程表
2. 按教学楼-教室整理课程
3. 生成每日提醒文本
4. 导出为iCalendar格式，可导入手机日历
"""

import os
import sys
import pandas as pd
from icalendar import Calendar, Event
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import filedialog, messagebox, ttk

# 常量定义
WEEKDAYS = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
TIME_SLOTS = {
    '1-2': {'start': (8, 0), 'end': (9, 40)},
    '3-4': {'start': (10, 0), 'end': (11, 40)},
    '5-6': {'start': (14, 0), 'end': (15, 40)},
    '7-8': {'start': (16, 0), 'end': (17, 40)},
    '9-10': {'start': (19, 0), 'end': (20, 40)}
}

class CourseScheduler:
    def __init__(self):
        self.courses = []
        self.file_path = None
        self.current_date = datetime.now()
    
    def load_excel_file(self, file_path):
        """从Excel文件加载课程表数据"""
        try:
            self.file_path = file_path
            df = pd.read_excel(file_path)
            return df
        except Exception as e:
            raise Exception(f"加载文件失败: {str(e)}")
    
    def parse_course_data(self, df):
        """解析课程数据"""
        self.courses = []
        
        # 尝试自动识别列
        columns = df.columns.tolist()
        
        # 根据常见的课程表格式进行解析
        for idx, row in df.iterrows():
            # 跳过空行或表头行
            if pd.isna(row.iloc[0]) or str(row.iloc[0]).strip() == '':
                continue
                
            course_name = str(row.iloc[0])  # 假设第一列是课程名
            
            # 遍历可能包含课程安排的列
            for col_idx, col_name in enumerate(columns[1:], 1):
                cell_value = row.iloc[col_idx]
                if pd.isna(cell_value):
                    continue
                
                # 解析单元格内容，提取课程信息
                cell_text = str(cell_value)
                # 尝试从单元格中提取上课时间、地点和教师信息
                # 假设格式如："周一1-2节|实验楼B302|张老师"
                if '|' in cell_text:
                    parts = cell_text.split('|')
                    if len(parts) >= 3:
                        time_info, location, teacher = parts[:3]
                    elif len(parts) == 2:
                        time_info, location = parts
                        teacher = "未知"
                    else:
                        time_info = parts[0]
                        location = "未知"
                        teacher = "未知"
                else:
                    # 简化处理：假设单元格只包含时间信息
                    time_info = cell_text
                    location = "未知"
                    teacher = "未知"
                
                # 解析时间信息
                for weekday in WEEKDAYS:
                    if weekday in time_info:
                        # 提取节次
                        for slot in TIME_SLOTS.keys():
                            if slot in time_info:
                                course = {
                                    'name': course_name,
                                    'weekday': weekday,
                                    'time_slot': slot,
                                    'location': location,
                                    'teacher': teacher
                                }
                                self.courses.append(course)
                                break
        
        # 如果没有解析到课程，尝试另一种常见格式
        if not self.courses:
            self._parse_alternative_format(df)
        
        return self.courses
    
    def _parse_alternative_format(self, df):
        """尝试解析另一种常见的课程表格式"""
        for idx, row in df.iterrows():
            for col_idx, cell_value in enumerate(row):
                if pd.isna(cell_value):
                    continue
                
                cell_text = str(cell_value)
                # 检查是否包含课程相关信息
                if any(weekday in cell_text for weekday in WEEKDAYS) and any(slot in cell_text for slot in TIME_SLOTS.keys()):
                    # 尝试提取课程名（通常在同一行的前几列）
                    course_name = "未知课程"
                    for i in range(min(col_idx, 3)):
                        if not pd.isna(row.iloc[i]) and str(row.iloc[i]).strip():
                            course_name = str(row.iloc[i])
                            break
                    
                    # 提取时间信息
                    for weekday in WEEKDAYS:
                        if weekday in cell_text:
                            for slot in TIME_SLOTS.keys():
                                if slot in cell_text:
                                    # 尝试提取地点和教师信息
                                    location = "未知"
                                    teacher = "未知"
                                    
                                    # 假设地点包含楼、教室等关键词
                                    location_keywords = ['楼', '教室', '实验室', '机房']
                                    teacher_keywords = ['老师', '教授', '副教授', '讲师']
                                    
                                    for kw in location_keywords:
                                        if kw in cell_text:
                                            # 简单提取包含关键词的部分作为地点
                                            parts = cell_text.split(kw)
                                            if len(parts) > 1:
                                                location = parts[0].split()[-1] + kw + parts[1].split()[0] if parts[1].split() else parts[0].split()[-1] + kw
                                            break
                                    
                                    for kw in teacher_keywords:
                                        if kw in cell_text:
                                            # 简单提取包含关键词的部分作为教师
                                            parts = cell_text.split(kw)
                                            if len(parts) > 1:
                                                teacher = parts[0].split()[-1] + kw
                                            break
                                    
                                    course = {
                                        'name': course_name,
                                        'weekday': weekday,
                                        'time_slot': slot,
                                        'location': location,
                                        'teacher': teacher
                                    }
                                    self.courses.append(course)
    
    def organize_by_location(self):
        """按教学楼-教室整理课程"""
        location_dict = {}
        
        for course in self.courses:
            location = course['location']
            # 提取教学楼信息（假设格式为"实验楼B302"，提取"实验楼"）
            building = None
            for kw in ['楼', '馆', '中心']:
                if kw in location:
                    idx = location.find(kw)
                    building = location[:idx+1]
                    break
            
            if building is None:
                building = '其他'
            
            if building not in location_dict:
                location_dict[building] = {}
            
            if location not in location_dict[building]:
                location_dict[building][location] = []
            
            location_dict[building][location].append(course)
        
        return location_dict
    
    def generate_daily_reminder(self, date=None):
        """生成指定日期的课程提醒"""
        if date is None:
            date = self.current_date
        
        weekday = WEEKDAYS[date.weekday()]
        today_courses = [c for c in self.courses if c['weekday'] == weekday]
        
        if not today_courses:
            return f"{date.strftime('%Y年%m月%d日')} {weekday} 今天没有课程，好好休息！"
        
        reminder = f"{date.strftime('%Y年%m月%d日')} {weekday} 课程提醒：\n"
        
        # 按时间顺序排序
        today_courses.sort(key=lambda x: list(TIME_SLOTS.keys()).index(x['time_slot']))
        
        has_morning_class = False
        for course in today_courses:
            start_hour, start_minute = TIME_SLOTS[course['time_slot']]['start']
            if start_hour < 12:
                has_morning_class = True
                if start_hour <= 8:
                    reminder += f"【早八提醒】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
                else:
                    reminder += f"【上午】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
            else:
                reminder += f"【下午/晚上】{course['name']} - {course['time_slot']}节 - {course['location']} - {course['teacher']}\n"
        
        return reminder
    
    def export_to_ical(self, output_path):
        """导出课程表为iCalendar格式"""
        cal = Calendar()
        cal.add('prodid', '-//Course Schedule//course_scheduler.py//')
        cal.add('version', '2.0')
        
        # 获取当前学期的开始日期（这里简化处理，假设从当前周的周一开始）
        today = datetime.now()
        start_of_week = today - timedelta(days=today.weekday())
        
        for course in self.courses:
            # 创建事件
            event = Event()
            event.add('summary', course['name'])
            event.add('description', f"教师：{course['teacher']}\n地点：{course['location']}")
            event.add('location', course['location'])
            
            # 计算事件的日期和时间
            weekday_idx = WEEKDAYS.index(course['weekday'])
            event_date = start_of_week + timedelta(days=weekday_idx)
            
            start_hour, start_minute = TIME_SLOTS[course['time_slot']]['start']
            end_hour, end_minute = TIME_SLOTS[course['time_slot']]['end']
            
            start_time = event_date.replace(hour=start_hour, minute=start_minute)
            end_time = event_date.replace(hour=end_hour, minute=end_minute)
            
            # 添加重复规则，每周重复一次
            event.add('dtstart', start_time)
            event.add('dtend', end_time)
            event.add('rrule', {'freq': 'weekly'})
            
            # 添加唯一标识符
            event.add('uid', f"{course['name']}-{course['weekday']}-{course['time_slot']}@course_scheduler")
            
            cal.add_component(event)
        
        # 保存到文件
        try:
            with open(output_path, 'wb') as f:
                f.write(cal.to_ical())
            return True
        except Exception as e:
            raise Exception(f"导出iCalendar文件失败: {str(e)}")
    
    def export_location_summary(self, output_path):
        """导出按地点整理的课程摘要"""
        location_dict = self.organize_by_location()
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write("按教学楼-教室整理的课程表\n")
                f.write("="*50 + "\n\n")
                
                for building in sorted(location_dict.keys()):
                    f.write(f"【{building}】\n")
                    f.write("-"*20 + "\n")
                    
                    for location in sorted(location_dict[building].keys()):
                        courses = location_dict[building][location]
                        f.write(f"  {location}:\n")
                        
                        # 按星期和时间排序
                        courses.sort(key=lambda x: (WEEKDAYS.index(x['weekday']), list(TIME_SLOTS.keys()).index(x['time_slot'])))
                        
                        for course in courses:
                            f.write(f"    {course['weekday']} {course['time_slot']}节: {course['name']} ({course['teacher']})\n")
                        
                        f.write("\n")
            
            return True
        except Exception as e:
            raise Exception(f"导出地点摘要文件失败: {str(e)}")

class CourseSchedulerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("课程表管理工具")
        self.root.geometry("800x600")
        self.root.resizable(True, True)
        
        self.scheduler = CourseScheduler()
        
        # 设置中文字体
        self.font_config()
        
        # 创建界面
        self.create_widgets()
    
    def font_config(self):
        """配置字体以确保中文正常显示"""
        # Windows系统通常支持这些字体
        default_font = ('Microsoft YaHei UI', 10)
        title_font = ('Microsoft YaHei UI', 12, 'bold')
        
        # 设置样式
        self.style = ttk.Style()
        self.style.configure('TLabel', font=default_font)
        self.style.configure('TButton', font=default_font)
        self.style.configure('TEntry', font=default_font)
        self.style.configure('TText', font=default_font)
        self.style.configure('TCombobox', font=default_font)
        self.style.configure('Header.TLabel', font=title_font)
    
    def create_widgets(self):
        """创建GUI组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text="文件选择", padding="10")
        file_frame.pack(fill=tk.X, pady=10)
        
        self.file_path_var = tk.StringVar()
        file_entry = ttk.Entry(file_frame, textvariable=self.file_path_var, width=60)
        file_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file)
        browse_btn.pack(side=tk.LEFT, padx=5)
        
        load_btn = ttk.Button(file_frame, text="加载课程表", command=self.load_course_schedule)
        load_btn.pack(side=tk.LEFT, padx=5)
        
        # 功能区域
        function_frame = ttk.LabelFrame(main_frame, text="功能", padding="10")
        function_frame.pack(fill=tk.X, pady=10)
        
        gen_reminder_btn = ttk.Button(function_frame, text="生成今日课程提醒", command=self.generate_today_reminder)
        gen_reminder_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        export_cal_btn = ttk.Button(function_frame, text="导出为日历文件", command=self.export_calendar)
        export_cal_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        export_location_btn = ttk.Button(function_frame, text="导出按地点整理的课程", command=self.export_location_courses)
        export_location_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text="结果", padding="10")
        result_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 创建文本框和滚动条
        self.result_text = tk.Text(result_frame, wrap=tk.WORD, height=20, font=('Microsoft YaHei UI', 10))
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        scrollbar = ttk.Scrollbar(result_frame, command=self.result_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5)
        
        self.result_text.config(yscrollcommand=scrollbar.set)
        
        # 状态区域
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def browse_file(self):
        """浏览并选择Excel文件"""
        file_path = filedialog.askopenfilename(
            title="选择课程表Excel文件",
            filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
        )
        
        if file_path:
            self.file_path_var.set(file_path)
    
    def load_course_schedule(self):
        """加载并解析课程表"""
        file_path = self.file_path_var.get()
        if not file_path:
            messagebox.showerror("错误", "请先选择课程表文件")
            return
        
        try:
            self.status_var.set("正在加载课程表...")
            self.root.update()
            
            df = self.scheduler.load_excel_file(file_path)
            courses = self.scheduler.parse_course_data(df)
            
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, f"成功加载课程表：{os.path.basename(file_path)}\n")
            self.result_text.insert(tk.END, f"共解析到 {len(courses)} 门课程\n\n")
            
            # 显示部分课程信息
            self.result_text.insert(tk.END, "课程列表（前10个）：\n")
            for i, course in enumerate(courses[:10]):
                self.result_text.insert(tk.END, f"{i+1}. {course['name']} - {course['weekday']} {course['time_slot']}节 - {course['location']}\n")
            
            if len(courses) > 10:
                self.result_text.insert(tk.END, f"... 还有 {len(courses) - 10} 门课程\n")
            
            self.status_var.set("课程表加载成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("加载失败")
    
    def generate_today_reminder(self):
        """生成今日课程提醒"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        try:
            reminder = self.scheduler.generate_daily_reminder()
            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, reminder)
            self.status_var.set("已生成今日课程提醒")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("生成提醒失败")
    
    def export_calendar(self):
        """导出课程表为iCalendar格式"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        # 默认文件名
        default_filename = "课程表.ics"
        if self.scheduler.file_path:
            base_name = os.path.splitext(os.path.basename(self.scheduler.file_path))[0]
            default_filename = f"{base_name}.ics"
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            title="保存日历文件",
            defaultextension=".ics",
            filetypes=[("iCalendar files", "*.ics"), ("All files", "*.*")],
            initialfile=default_filename
        )
        
        if not save_path:
            return
        
        try:
            self.status_var.set("正在导出日历文件...")
            self.root.update()
            
            success = self.scheduler.export_to_ical(save_path)
            if success:
                messagebox.showinfo("成功", f"日历文件已导出到：{save_path}")
                self.status_var.set("日历文件导出成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("导出失败")
    
    def export_location_courses(self):
        """导出按地点整理的课程"""
        if not self.scheduler.courses:
            messagebox.showwarning("警告", "请先加载课程表")
            return
        
        # 默认文件名
        default_filename = "课程地点整理.txt"
        if self.scheduler.file_path:
            base_name = os.path.splitext(os.path.basename(self.scheduler.file_path))[0]
            default_filename = f"{base_name}_地点整理.txt"
        
        # 选择保存路径
        save_path = filedialog.asksaveasfilename(
            title="保存地点整理文件",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("All files", "*.*")],
            initialfile=default_filename
        )
        
        if not save_path:
            return
        
        try:
            self.status_var.set("正在导出地点整理文件...")
            self.root.update()
            
            success = self.scheduler.export_location_summary(save_path)
            if success:
                messagebox.showinfo("成功", f"地点整理文件已导出到：{save_path}")
                self.status_var.set("地点整理文件导出成功")
        
        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.status_var.set("导出失败")

def main():
    # 检查Python版本
    if sys.version_info < (3, 6):
        print("本程序需要Python 3.6或更高版本")
        sys.exit(1)
    
    # 创建GUI应用
    root = tk.Tk()
    app = CourseSchedulerApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()